{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c3e2458f-d038-4845-93a0-d4ad830f9f90",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Chains\n",
    "\n",
    "对于简单的大模型应用，单独使用语言模型（LLMs）是可以的。\n",
    "\n",
    "**但更复杂的大模型应用需要将 `LLMs` 和 `Chat Models` 链接在一起 - 要么彼此链接，要么与其他组件链接。**\n",
    "\n",
    "LangChain 为这种“链式”应用程序提供了 `Chain` 接口。\n",
    "\n",
    "LangChain 以通用方式定义了 `Chain`，它是对组件进行调用序列的集合，其中可以包含其他链。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c81a7df0-26c7-4eb8-92f1-cc54445cf507",
   "metadata": {},
   "source": [
    "## LLMChain\n",
    "\n",
    "LLMChain 是 LangChain 中最简单的链，作为其他复杂 Chains 和 Agents 的内部调用，被广泛应用。\n",
    "\n",
    "一个LLMChain由PromptTemplate和语言模型（LLM or Chat Model）组成。它使用直接传入（或 memory 提供）的 key-value 来规范化生成 Prompt Template（提示模板），并将生成的 prompt （格式化后的字符串）传递给大模型，并返回大模型输出。\n",
    "\n",
    "![](../images/llm_chain.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fbd5ca7-ca54-4701-919c-2857266caefc",
   "metadata": {},
   "source": [
    "## Router Chain: 实现条件判断的大模型调用\n",
    "\n",
    "\n",
    "这段代码构建了一个可定制的链路系统，用户可以提供不同的输入提示，并根据这些提示获取适当的响应。\n",
    "\n",
    "主要逻辑：从`prompt_infos`创建多个`LLMChain`对象，并将它们保存在一个字典中，然后创建一个默认的`ConversationChain`，最后创建一个带有路由功能的`MultiPromptChain`。\n",
    "\n",
    "![](../images/router_chain.png)"
   ]
  },
  {
   "cell_type": "code",
   "id": "aaf8c391-9225-4e66-ad4d-d689b53a0379",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T01:36:44.866133Z",
     "start_time": "2024-07-12T01:36:44.850550Z"
    }
   },
   "source": [
    "from langchain.chains.router import MultiPromptChain\n",
    "from langchain_openai import OpenAI\n",
    "from langchain.chains.conversation.base import ConversationChain\n",
    "from langchain.chains.llm import LLMChain\n",
    "from langchain.prompts import PromptTemplate"
   ],
   "outputs": [],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "id": "33b5061c-391e-4762-91c7-73b57f4ab501",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:36:57.502447Z",
     "start_time": "2024-07-12T02:36:57.495013Z"
    }
   },
   "source": [
    "physics_template = \"\"\"你是一位非常聪明的物理教授。\n",
    "你擅长以简洁易懂的方式回答关于物理的问题。\n",
    "当你不知道某个问题的答案时，你会坦诚承认。\n",
    "\n",
    "这是一个问题：\n",
    "{input}\"\"\"\n",
    "\n",
    "\n",
    "math_template = \"\"\"你是一位很棒的数学家。你擅长回答数学问题。\n",
    "之所以如此出色，是因为你能够将难题分解成各个组成部分，\n",
    "先回答这些组成部分，然后再将它们整合起来回答更广泛的问题。\n",
    "\n",
    "这是一个问题：\n",
    "{input}\"\"\"\n",
    "\n",
    "# 生物老师\n",
    "biology_template = \"\"\"你是一位很棒的生物老师。\n",
    "你擅长以简洁易懂的方式回答关于生物学的问题。\n",
    "当你不知道某个问题的答案时，你会坦诚承认。\n",
    "\n",
    "这是一个问题：\n",
    "{input}\"\"\"\n",
    "# 计算机老师\n",
    "computer_template = \"\"\"你是一位很棒的计算机老师。\n",
    "你擅长以简洁易懂的方式回答关于计算机的问题。\n",
    "当你不知道某个问题的答案时，你会坦诚承认。\n",
    "\n",
    "这是一个问题：\n",
    "{input}\"\"\"\n",
    "\n",
    "# 汉语言文学老师\n",
    "chinese_template = \"\"\"你是一位很棒的汉语言文学老师。\n",
    "你擅长以简洁易懂的方式回答关于汉语言文学的问题。\n",
    "当你不知道某个问题的答案时，你会坦诚承认。\n",
    "\n",
    "这是一个问题：\n",
    "{input}\"\"\"\n"
   ],
   "outputs": [],
   "execution_count": 32
  },
  {
   "cell_type": "code",
   "id": "5ef1db6e-3da4-4f9b-9707-0f30aa293dd7",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:36:59.050190Z",
     "start_time": "2024-07-12T02:36:59.041068Z"
    }
   },
   "source": [
    "prompt_infos = [\n",
    "    {\n",
    "        \"name\": \"物理\",\n",
    "        \"description\": \"适用于回答物理问题\",\n",
    "        \"prompt_template\": physics_template,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"数学\",\n",
    "        \"description\": \"适用于回答数学问题\",\n",
    "        \"prompt_template\": math_template,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"生物\",\n",
    "        \"description\": \"适用于回答生物问题\",\n",
    "        \"prompt_template\": biology_template,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"计算机\",\n",
    "        \"description\": \"适用于回答计算机问题\",\n",
    "        \"prompt_template\": computer_template,\n",
    "    },\n",
    "    {\n",
    "        \"name\": \"汉语言文学\",\n",
    "        \"description\": \"适用于回答汉语言文学问题\",\n",
    "        \"prompt_template\": chinese_template,\n",
    "    },\n",
    "]"
   ],
   "outputs": [],
   "execution_count": 33
  },
  {
   "cell_type": "code",
   "id": "3983cafe-c2d5-4951-b779-88d844594777",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:02.877082Z",
     "start_time": "2024-07-12T02:37:01.939684Z"
    }
   },
   "source": [
    "llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\")"
   ],
   "outputs": [],
   "execution_count": 34
  },
  {
   "cell_type": "code",
   "id": "db8be9f0-1ac2-4ded-8950-6403cfa40004",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:04.471655Z",
     "start_time": "2024-07-12T02:37:04.447696Z"
    }
   },
   "source": [
    "# 创建一个空的目标链字典，用于存放根据prompt_infos生成的LLMChain。\n",
    "destination_chains = {}\n",
    "\n",
    "# 遍历prompt_infos列表，为每个信息创建一个LLMChain。\n",
    "for p_info in prompt_infos:\n",
    "    name = p_info[\"name\"]  # 提取名称\n",
    "    prompt_template = p_info[\"prompt_template\"]  # 提取模板\n",
    "    # 创建PromptTemplate对象\n",
    "    prompt = PromptTemplate(template=prompt_template, input_variables=[\"input\"])\n",
    "    # 使用上述模板和llm对象创建LLMChain对象\n",
    "    chain = LLMChain(llm=llm, prompt=prompt)\n",
    "    # 将新创建的chain对象添加到destination_chains字典中\n",
    "    destination_chains[name] = chain\n",
    "\n",
    "# 创建一个默认的ConversationChain,兜底的，相当于if else语句中的最后一个else\n",
    "default_chain = ConversationChain(llm=llm, output_key=\"text\")"
   ],
   "outputs": [],
   "execution_count": 35
  },
  {
   "cell_type": "code",
   "id": "ae77b13a-2077-4e80-83f9-a2b1d8398461",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:08.111665Z",
     "start_time": "2024-07-12T02:37:08.095970Z"
    }
   },
   "source": [
    "type(default_chain)"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "langchain.chains.conversation.base.ConversationChain"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 36
  },
  {
   "cell_type": "markdown",
   "id": "f5aa4a82-2d96-4124-8896-4e11e5d5c8e9",
   "metadata": {},
   "source": [
    "### 使用 LLMRouterChain 实现条件判断调用\n",
    "\n",
    "这段代码定义了一个chain对象（LLMRouterChain），该对象首先使用router_chain来决定哪个destination_chain应该被执行，如果没有合适的目标链，则默认使用default_chain。"
   ]
  },
  {
   "cell_type": "code",
   "id": "1c196e6c-e767-4d4f-8327-50ead641bc3a",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:10.767563Z",
     "start_time": "2024-07-12T02:37:10.761970Z"
    }
   },
   "source": [
    "from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser\n",
    "from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE"
   ],
   "outputs": [],
   "execution_count": 37
  },
  {
   "cell_type": "code",
   "id": "f5ada86e-e430-412c-828d-b053b630f07c",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:12.674759Z",
     "start_time": "2024-07-12T02:37:12.658525Z"
    }
   },
   "source": [
    "# 从prompt_infos中提取目标信息并将其转化为字符串列表\n",
    "destinations = [f\"{p['name']}: {p['description']}\" for p in prompt_infos]\n",
    "# 使用join方法将列表转化为字符串，每个元素之间用换行符分隔\n",
    "destinations_str = \"\\n\".join(destinations)\n",
    "# 根据MULTI_PROMPT_ROUTER_TEMPLATE格式化字符串和destinations_str创建路由模板\n",
    "router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)\n",
    "# 创建路由的PromptTemplate\n",
    "router_prompt = PromptTemplate(\n",
    "    template=router_template,\n",
    "    input_variables=[\"input\"],\n",
    "    output_parser=RouterOutputParser(),\n",
    ")\n",
    "# 使用上述路由模板和llm对象创建LLMRouterChain对象\n",
    "router_chain = LLMRouterChain.from_llm(llm, router_prompt)"
   ],
   "outputs": [],
   "execution_count": 38
  },
  {
   "cell_type": "code",
   "id": "8c1013dc-ae1f-468d-96b3-4babe0d50d1f",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:16.130915Z",
     "start_time": "2024-07-12T02:37:16.121410Z"
    }
   },
   "source": "print(destinations)",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['物理: 适用于回答物理问题', '数学: 适用于回答数学问题', '生物: 适用于回答生物问题', '计算机: 适用于回答计算机问题', '汉语言文学: 适用于回答汉语言文学问题']\n"
     ]
    }
   ],
   "execution_count": 39
  },
  {
   "cell_type": "code",
   "id": "a85ef126-aca1-40c2-8e01-d15af5500785",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:21.976511Z",
     "start_time": "2024-07-12T02:37:21.957416Z"
    }
   },
   "source": [
    "print(destinations_str)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "物理: 适用于回答物理问题\n",
      "数学: 适用于回答数学问题\n",
      "生物: 适用于回答生物问题\n",
      "计算机: 适用于回答计算机问题\n",
      "汉语言文学: 适用于回答汉语言文学问题\n"
     ]
    }
   ],
   "execution_count": 40
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:29.215896Z",
     "start_time": "2024-07-12T02:37:29.200731Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 每次format都会去掉模板中的变量的n/2个花括号，其中n是变量的花括号数据量（n>1）\n",
    "\"测试：{{{{china}}}} {hello}, {{book}}\".format(hello=\"hello\")"
   ],
   "id": "a2e0780c97ca51d",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'测试：{{china}} hello, {book}'"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 41
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:31.097176Z",
     "start_time": "2024-07-12T02:37:31.082072Z"
    }
   },
   "cell_type": "code",
   "source": "print(MULTI_PROMPT_ROUTER_TEMPLATE)",
   "id": "5db81fcb-704a-4250-a6b5-210e4be77af5",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Given a raw text input to a language model select the model prompt best suited for the input. You will be given the names of the available prompts and a description of what the prompt is best suited for. You may also revise the original input if you think that revising it will ultimately lead to a better response from the language model.\n",
      "\n",
      "<< FORMATTING >>\n",
      "Return a markdown code snippet with a JSON object formatted to look like:\n",
      "```json\n",
      "{{{{\n",
      "    \"destination\": string \\ name of the prompt to use or \"DEFAULT\"\n",
      "    \"next_inputs\": string \\ a potentially modified version of the original input\n",
      "}}}}\n",
      "```\n",
      "\n",
      "REMEMBER: \"destination\" MUST be one of the candidate prompt names specified below OR it can be \"DEFAULT\" if the input is not well suited for any of the candidate prompts.\n",
      "REMEMBER: \"next_inputs\" can just be the original input if you don't think any modifications are needed.\n",
      "\n",
      "<< CANDIDATE PROMPTS >>\n",
      "{destinations}\n",
      "\n",
      "<< INPUT >>\n",
      "{{input}}\n",
      "\n",
      "<< OUTPUT (must include ```json at the start of the response) >>\n",
      "<< OUTPUT (must end with ```) >>\n",
      "\n"
     ]
    }
   ],
   "execution_count": 42
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:32.813034Z",
     "start_time": "2024-07-12T02:37:32.795921Z"
    }
   },
   "cell_type": "code",
   "source": "print(router_template)",
   "id": "f882244c-1fa6-4d74-a44c-578c9fb25e18",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Given a raw text input to a language model select the model prompt best suited for the input. You will be given the names of the available prompts and a description of what the prompt is best suited for. You may also revise the original input if you think that revising it will ultimately lead to a better response from the language model.\n",
      "\n",
      "<< FORMATTING >>\n",
      "Return a markdown code snippet with a JSON object formatted to look like:\n",
      "```json\n",
      "{{\n",
      "    \"destination\": string \\ name of the prompt to use or \"DEFAULT\"\n",
      "    \"next_inputs\": string \\ a potentially modified version of the original input\n",
      "}}\n",
      "```\n",
      "\n",
      "REMEMBER: \"destination\" MUST be one of the candidate prompt names specified below OR it can be \"DEFAULT\" if the input is not well suited for any of the candidate prompts.\n",
      "REMEMBER: \"next_inputs\" can just be the original input if you don't think any modifications are needed.\n",
      "\n",
      "<< CANDIDATE PROMPTS >>\n",
      "物理: 适用于回答物理问题\n",
      "数学: 适用于回答数学问题\n",
      "生物: 适用于回答生物问题\n",
      "计算机: 适用于回答计算机问题\n",
      "汉语言文学: 适用于回答汉语言文学问题\n",
      "\n",
      "<< INPUT >>\n",
      "{input}\n",
      "\n",
      "<< OUTPUT (must include ```json at the start of the response) >>\n",
      "<< OUTPUT (must end with ```) >>\n",
      "\n"
     ]
    }
   ],
   "execution_count": 43
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "",
   "id": "e33980479c310ac2"
  },
  {
   "cell_type": "code",
   "id": "c2a482e4-5757-4295-a3d8-c3fdd1d4abd2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:37:35.212342Z",
     "start_time": "2024-07-12T02:37:35.202333Z"
    }
   },
   "source": [
    "# 创建MultiPromptChain对象，其中包含了路由链，目标链和默认链。\n",
    "chain = MultiPromptChain(\n",
    "    router_chain=router_chain,\n",
    "    destination_chains=destination_chains,\n",
    "    default_chain=default_chain,\n",
    "    verbose=True,\n",
    ")"
   ],
   "outputs": [],
   "execution_count": 44
  },
  {
   "cell_type": "code",
   "id": "7ad5dcb2-48c0-4d0f-b6cc-09ebcbdce75e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:38:20.831267Z",
     "start_time": "2024-07-12T02:38:20.818500Z"
    }
   },
   "source": [
    "router_chain.verbose = True"
   ],
   "outputs": [],
   "execution_count": 47
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "物理: {'input': '黑体辐射是什么？?'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "黑体辐射是一种理想的物理现象，它指的是处于热平衡状态、不透光且完全吸收所有进入的辐射的物体所发射出的辐射。根据普朗克定律，黑体辐射的光谱分布与温度有关，即温度越高，发射的光谱分布也会更强。这个现象对于理解热辐射和能量传输有重要意义。\n"
     ]
    }
   ],
   "execution_count": 45,
   "source": "print(chain.run(\"黑体辐射是什么？?\"))",
   "id": "128bb7a0-b176-4b14-835e-8aaa723ab441"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "数学: {'input': '找到第一个大于40的质数，使得这个质数加一能被3整除。'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "首先，我们需要了解质数的定义。质数是指只能被1和自身整除的数，也就是除了1和它本身之外没有其他因数的数。\n",
      "\n",
      "根据题目要求，我们需要找到一个大于40的质数，所以我们可以从41开始检查数是否为质数。\n",
      "\n",
      "首先，我们可以将41除以2，发现余数为1，所以41不是偶数，也就是说41不可能被2整除，因此41可能是一个质数。\n",
      "\n",
      "然后，我们可以继续将41除以3，发现余数为2，所以41也不可能被3整除，因此41也不是一个能被3整除的数。\n",
      "\n",
      "接着，我们可以将41除以5，发现余数为1，所以41也不可能被5整除，因此41也不是一个能被5整除的数。\n",
      "\n",
      "继续按照这个方法，我们可以将41分别除以7\n"
     ]
    }
   ],
   "execution_count": 46,
   "source": [
    "print(\n",
    "    chain.run(\n",
    "        \"大于40的第一个质数是多少，使得这个质数加一能被3整除？\"\n",
    "    )\n",
    ")"
   ],
   "id": "cd869807-9cec-4bb2-9104-ecc4efce9baa"
  },
  {
   "cell_type": "code",
   "id": "bd37e004-bb24-4929-992c-34407593d86e",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:27:56.055155Z",
     "start_time": "2024-07-12T02:27:49.779278Z"
    }
   },
   "source": [
    "print(chain.run(\"黑洞是什么？\"))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMRouterChain chain...\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "物理: {'input': '黑洞是什么？'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "黑洞是宇宙中非常密集的物质聚集体，其引力非常强大，甚至连光也无法逃离它的吸引。它的引力是由其质量和体积决定的，通常被描述为一个密度极高、质量极大的奇点。我们无法直接观测黑洞，但我们可以通过它们产生的引力效应来研究它们。黑洞是一种非常有趣的天体，也是物理学中重要的研究对象。\n"
     ]
    }
   ],
   "execution_count": 29
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:39:04.955178Z",
     "start_time": "2024-07-12T02:38:58.456172Z"
    }
   },
   "cell_type": "code",
   "source": "print(chain.run(\"硬盘的分类有哪些?\"))",
   "id": "97e5b7bbda2ca6a4",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMRouterChain chain...\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "计算机: {'input': '硬盘的分类有哪些?'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "硬盘可以按照存储介质、接口类型、转速等多种方式进行分类。最常见的分类方式有以下几类：\n",
      "1. 根据存储介质：固态硬盘（SSD）和机械硬盘（HDD）两类。\n",
      "2. 根据接口类型：SATA、SAS、SCSI等。\n",
      "3. 根据转速：7200转、10000转、15000转等。\n",
      "4. 根据外形尺寸：2.5英寸、3.5英寸等。\n",
      "5. 根据用途：普通硬盘、服务器硬盘、笔记本硬盘、移动硬盘等。\n"
     ]
    }
   ],
   "execution_count": 48
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:40:35.765334Z",
     "start_time": "2024-07-12T02:40:29.713699Z"
    }
   },
   "cell_type": "code",
   "source": "print(chain.run(\"人的祖先是什么?\"))",
   "id": "8a0b7c55164dd096",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMRouterChain chain...\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "生物: {'input': '人的祖先'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "是什么？\n",
      "\n",
      "这是一个很复杂的问题，因为人的祖先可以追溯到几百万年前的灵长类动物。根据科学研究，最早的人类祖先可以追溯到大约600万年前的非洲地区。这些祖先是类人猿，与今天的黑猩猩和大猩猩有着共同的祖先。随着进化的过程，这些类人猿逐渐发展成为早期的人类，包括直立人和尼安德特人。最后，大约20万年前，现代人类（智人）出现并逐渐取代其他早期人类。所以，人类的祖先可以说是非洲的类人猿。但是，这只是一个简单的概括，实际情况可能更复杂，科\n"
     ]
    }
   ],
   "execution_count": 49
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:40:46.019044Z",
     "start_time": "2024-07-12T02:40:41.601906Z"
    }
   },
   "cell_type": "code",
   "source": "print(chain.run(\"李白最出名的诗有哪些?\"))",
   "id": "b6e04d63a08ee7e6",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMRouterChain chain...\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "汉语言文学: {'input': '李白最出名的诗有哪些?'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "\n",
      "\n",
      "李白最出名的诗包括《将进酒》、《静夜思》、《早发白帝城》、《望庐山瀑布》等。这些诗作都是他的代表作，被广泛传诵和赞美。不过，李白的诗作数量众多，每个人认为最出名的诗可能会有所不同。\n"
     ]
    }
   ],
   "execution_count": 50
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2024-07-12T02:43:02.422003Z",
     "start_time": "2024-07-12T02:42:55.882681Z"
    }
   },
   "cell_type": "code",
   "source": "print(chain.run(\"红烧肉的做法?\"))",
   "id": "12f3086e0fb46e9b",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001B[1m> Entering new MultiPromptChain chain...\u001B[0m\n",
      "\n",
      "\n",
      "\u001B[1m> Entering new LLMRouterChain chain...\u001B[0m\n",
      "\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      "None: {'input': '红烧肉的做法?'}\n",
      "\u001B[1m> Finished chain.\u001B[0m\n",
      " 红烧肉是一道中国菜肴，主要由猪肉（通常是五花肉）和配料（如酱油、糖、料酒、姜等）烧制而成。首先将猪肉切成小块，然后用冷水煮沸，撇去浮沫。接着将肉块捞出，用冷水冲洗干净，备用。将炒锅烧热，放入适量的油，然后加入葱姜蒜炒香。接着放入肉块煸炒，直到肉块变色。然后加入适量的酱油、糖和料酒，翻炒均匀。接着加入适量的水，盖上盖子，用小火煨烧约\n"
     ]
    }
   ],
   "execution_count": 54
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a51119ed-025f-48d7-ad81-cd9cdab7090f",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cbda2930-a0e6-48b2-8e02-4c3d792f0225",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "24d11e0f-d5ee-4086-9e1a-b21000232134",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "8b6836f0-213d-4cac-abc9-3617831be3db",
   "metadata": {},
   "source": [
    "### Homework\n",
    "\n",
    "#### （已完成）扩展 Demo：实现生物、计算机和汉语言文学老师 PromptTemplates 及对应 Chains"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c7edb0a-675d-40c0-9f5d-d58f0170ce72",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
